package com.chandon.juc.thread_basic;

import java.text.DecimalFormat;
import java.util.Random;
import java.util.concurrent.CountDownLatch;

/**
 * 1.工作线程数是不是设置的越大越好？
 * 2.工作线程数或线程池中的线程设置多少合适？
 *
 * 场景：一千万个随机数的和
 * m1方法for循环产生一千万个随机数累加，
 * m2方法使用2个线程分别计算一半的和，最后相加
 * m3方法使用N个线程分段计算和，最后相加
 *
 * 测试机器配置：4核心8线程
 *  第一次：m1--101  m2--57 m3--63（4线程）
 *  第二次：m1--100  m2--55 m3--57（8线程）
 *  第三次：m1--103  m2--56 m3--85（16线程）
 *  第四次：m1--102  m2--46 m3--222(128线程)
 *  第四次：m1--109  m2--47 m3--259(1280线程)
 *  第四次：m1--102  m2--46 m3--1261(12800线程)
 *
 *  1.可见线程数并不是越大越好。开启过多的线程造成cpu频繁切换，线程的切换也是消耗资源的
 *  2.测试中可见到m1 m2多次执行所需要的时间差别并不大。
 *    m3中线程的数量从128-1280的所用时间差别也并不是太大，到底设置多少合适？
 *    通过压测确定：设置不同的线程数，不断的压测，最终找出一个最合适的值。
 *    推算确定：根据cpu性能设定，比如4核心的cpu，我设定4个线程。
 *      但是也不能完全保证合适，同时运行的线程还有系统等别的线程，cpu不一定在此程序中得到充分压榨。
 *
 *    出于安全角度，一般cpu利用率达到80%，实际还是需要通过压测来最终确定线程数量。
 *    关于线程数量的公式：
 *    线程数=cpu核心数*cpu利用率*(等待时间/计算时间)
 *    等待时间：IO资源调度等
 *    如何知道线程cpu等待时间与cpu计算时间？
 *      只能将程序部署完成后，统计结果，任何推算都不可能很准确。
 *      本地测试开发使用Profiler(性能分析)工具测算，麻烦点的，在程序中记录执行时间。生产环境中可使用阿里arthas
 *      分布式系统中参考springcloud内容
 *
 *
 *
 *
 * @author Dong
 * @version 1.0
 * @date 2021-04-02 21:35
 */

public class T00_MultiThread_VS_SingleThread_And_ContextSwitch {

    public static void main(String[] args) throws Exception {
        m1();
        m2();
        m3();
    }

    //===================================================
    private static double[] nums = new double[10_0000_000];
    private static Random r = new Random();
    private static DecimalFormat df = new DecimalFormat("0.00");

    static {
        for (int i = 0; i < nums.length; i++) {
            nums[i] = r.nextDouble();
        }
    }

    private static void m1() {
        long start = System.currentTimeMillis();

        double result = 0.0;
        for (int i = 0; i < nums.length; i++) {
            result += nums[i];
        }

        long end = System.currentTimeMillis();

        System.out.println("m1: " + (end - start) + " result = " + df.format(result));
    }

    //=======================================================
    static double result1 = 0.0, result2 = 0.0, result = 0.0;

    private static void m2() throws Exception {

        Thread t1 = new Thread(() -> {
            for (int i = 0; i < nums.length / 2; i++) {
                result1 += nums[i];
            }
        });
        Thread t2 = new Thread(() -> {
            for (int i = nums.length / 2; i < nums.length; i++) {
                result2 += nums[i];
            }
        });

        long start = System.currentTimeMillis();
        t1.start();
        t2.start();
        t1.join();
        t2.join();

        result = result1 + result2;

        long end = System.currentTimeMillis();

        System.out.println("m2: " + (end - start) + " result = " + df.format(result));
    }

    //===================================================================

    private static void m3() throws Exception {

        final int threadCount = 4;
        Thread[] threads = new Thread[threadCount];
        double[] results = new double[threadCount];
        final int segmentCount = nums.length / threadCount;
        CountDownLatch latch = new CountDownLatch(threadCount);

        for (int i = 0; i < threadCount; i++) {
            int m = i;

            threads[i] = new Thread(() -> {
                for (int j = m * segmentCount; j < (m + 1) * segmentCount && j < nums.length; j++) {
                    results[m] += nums[j];
                }
                latch.countDown();
            });

        }

        double resultM3 = 0.0;

        long start = System.currentTimeMillis();
        for (Thread t : threads) {
            t.start();
        }

        latch.await();
        for (int i = 0; i < results.length; i++) {
            resultM3 += results[i];
        }


        long end = System.currentTimeMillis();

        System.out.println("m3: " + (end - start) + " result = " + df.format(result));
    }


}
